import { Link } from '@brillout/docpress'

**Environment**: server.

We use `shield()` to guarantee the type of telefunction arguments. (As explained in <Link href="/RPC" />,
telefunctions are public and need protection.)

```js
// CreateTodo.telefunc.js
// Environment: server

export { onNewTodo }

import { shield } from 'telefunc'
const t = shield.type

shield(onNewTodo, [t.string])
async function onNewTodo(text) {
  // `text` is guaranteed to be a `string`: if `onNewTodo(42)` is called then Telefunc
  // throws an error that `text` should be a `string` (instead of a `number`)
}
```

> If we use TypeScript, then Telefunc automatically defines `shield()`, see <Link href="#typescript-automatic" />.


## TypeScript - Automatic

If we use TypeScript, then Telefunc automatically generates `shield()` for each telefunction.

In other words: telefunction argument types are automatically validated at runtime:

```ts ts-only
// hello.telefunc.ts

// We don't need to define a shield() when using TypeScript: Telefunc automatically generates
// it for us. For example here, Telefunc automatically aborts the telefunction call if the
// argument is `hello({ name: 42 })` and throws an error that `name` should be a `number`.
export async function hello({ name }: { name: string }) {
  return `Welcome to Telefunc, ${name}.`
}
```

With Telefunc, not only can we seamlessly re-use types across our frontend and backend code, but we also get automatic type-safety at runtime. If we use a TypeScript ORM (e.g. [Prisma](https://www.prisma.io/)) or SQL builder (e.g. [Kysely](https://github.com/koskimas/kysely) and [others](https://github.com/stars/brillout/lists/sql)), then we get end-to-end type safety all the way from database to frontend.

> For a faster development, Telefunc doesn't generate `shield()` and your telefunction arguments aren't validated during development.
> Telefunc only generates `shield()` upon building your app for production.
> You can enable the generation of `shield()` for development by setting <Link href="/shield-config">`config.shield.dev`</Link> to `true`.

> Telefunc's automatic `shield()` generation only works for stacks that transpile server-side code (Next.js, Vite, Vike, SvelteKit, Nuxt, etc.).
>
> For stacks that don't transpile server-side code (e.g. React Native and Parcel), we need to define `shield()` manually ourselves: see <Link href="#typescript-manual" />.


## TypeScript - Manual

If we define `shield()` manually (instead of using Telefunc's automatic `shield()` generator as described in <Link href="#typescript-automatic" />), then note that we don't need to define the arguments type twice:

```ts ts-only
import { shield } from 'telefunc'

export const onNewTodo = shield(
  [shield.type.string],
  async function (text) {
    // ✅ TypeScript knows that `text` is of type `string`
  }
)
```

Note that the following doesn't work:

```ts ts-only
import { shield } from 'telefunc'

shield(onNewTodo, [shield.type.string])
// TypeScript cannot infer the type of named functions by design.
export async function onNewTodo(text) {
  // ❌ TypeScript doesn't know that `text` is of type `string`
}
```


## Common types

Examples showcasing the most common `shield()` types:

```js
// TodoList.telefunc.js
// Environment: server

import { shield } from 'telefunc'
const t = shield.type

shield(onTextChange, [t.number, t.string])
async function onTextChange(id, text) {
  // typeof id === 'number'
  // typeof text === 'string'
}

shield(onCompletedToggle, [{ id: t.number, isCompleted: t.boolean }])
async function onCompletedToggle({ id, isCompleted }) {
  // typeof id === 'number'
  // typeof isCompleted === 'boolean'
}

shield(onTagListChange, [t.array(t.string)])
async function onTagListChange(tagList) {
  // tagList.every(tagName => typeof tagName === 'string')
}

shield(onNewMilestone, [{
  name: t.string,
  deadline: t.nullable(t.date),
  ownerId: t.optional(t.number)
}])
async function onNewMilestone({ name, deadline, ownerId }) {
  // typeof name === 'string'
  // deadline === null || deadline.constructor === Date
  // ownerId === undefined || typeof ownerId === 'number'
}

shield(onStatusChange, [t.or(
  t.const('DONE'),
  t.const('PROGRESS'),
  t.const('POSTPONED')
)])
async function onStatusChange(status) {
  // status === 'DONE' || status === 'PROGRESS' || status === 'POSTPONED'
}
```


## All types

List of `shield()` types:

`const t = shield.type` | TypeScript | JavaScript
-|-|-
`t.string` | `string` | `typeof value === 'string'`
`t.number` | `number` | `typeof value === 'number'`
`t.boolean` | `boolean` | <code>value === true &#x7c;&#x7c; value === false</code>
`t.date` | `Date` | `value.constructor === Date`
`t.array(T)` | `T[]` | `value.every(element => isT(element))`
`t.object(T)` | `Record<string, T>` | `Object.values(value).every(v => isT(v))`
`{ k1: T1, k2: T2, ... }` | <code>{'{ k1: T1, k2: T2, ... }'}</code> | <code>isT1(value.k1) && isT2(value.k2) && ...</code>
`t.or(T1, T2, ...)` | <code>T1 &#x7c; T2 &#x7c; ...</code> | <code>isT1(value) &#x7c;&#x7c; isT2(value) &#x7c;&#x7c; ...</code>
`t.tuple(T1, T2, ...)` | `[T1, T2, ...]` | `isT1(value[0]) && isT2(value[1]) && ...`
`t.const(val)` | `val as const` | `value === val`
`t.optional(T)` | <code>T &#x7c; undefined</code> | <code>isT(value) &#x7c;&#x7c; value === undefined</code>
`t.nullable(T)` | <code>T &#x7c; null</code> | <code>isT(value) &#x7c;&#x7c; value === null</code>
`t.any` | `any` | `true`
